package com.weds.frame.mq.aware;

import com.weds.frame.mq.constants.MqConstants;
import com.weds.frame.mq.util.PatternUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;

/**
 * Description:
 * Author: LiuWenQing
 * Datetime: 2023/3/21 17:11
 */
@Configuration
@Slf4j
public class BeanRegisterAutoConf {

    public BeanRegisterAutoConf(ApplicationContext applicationContext) {
        registerManualBean((ConfigurableApplicationContext) applicationContext);
    }

    /**
     * 手动注册自定义地bean
     *
     * @param applicationContext
     */
    private void registerManualBean(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String property = environment.getProperty("mq.active");
        String exchangeStr = environment.getProperty("mq.exchanges");
        String queueStr = environment.getProperty("mq.queues");
        List<List<String>> lists = PatternUtil.handleQueue(queueStr);
        if(StringUtils.isEmpty(exchangeStr) && StringUtils.isEmpty(queueStr))
            return ;
        log.info("开启mq配置");
        if ("true".equals(property)) {
            if (StringUtils.isEmpty(exchangeStr) ||
                    StringUtils.isEmpty(queueStr) ||
                    (exchangeStr.split(",").length != lists.size())) {
                log.error("mq配置异常，请检查队交换机和队列是否一一对应");
                return;
            }
            List<String> exchanges = Arrays.asList(exchangeStr.split(","));

            log.info("开始动态注入配置的mq交换机及队列,数量 {} ", exchanges.size());

            RabbitAdmin rabbitAdmin = (RabbitAdmin) applicationContext.getBean("rabbitAdmin");
            for (String ex : exchanges) {
                try {
                    String[] s = ex.split("-");
                    //product模式的才创建交换机
                    if (Arrays.asList(MqConstants.EXCHANGE_TYPES).contains(s[1])
                            && s[2].equals("p")) {
                        switch (s[1]) {
                            case "fanout":
                                FanoutExchange fanoutExchange = registerBean(applicationContext, s[0].contains("Exchange") ? s[0] : s[0] + "Exchange", FanoutExchange.class, s[0].contains("Exchange") ? s[0] : s[0] + "Exchange");
                                rabbitAdmin.declareExchange(fanoutExchange);
                                break;
                            case "direct":
                                break;
                            case "topic":
                                TopicExchange topicExchange = registerBean(applicationContext, s[0].contains("Exchange") ? s[0] : s[0] + "Exchange", TopicExchange.class, s[0].contains("Exchange") ? s[0] : s[0] + "Exchange");
                                rabbitAdmin.declareExchange(topicExchange);
                                break;
                            case "delay":
                                break;
                            case "formatDelay":
                                break;
                            default: {
                            }
                        }
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                    continue;
                }
            }
            Integer index = 0;
            for (List<String> l : lists) {
                try {
                    for (String o : l) {
                        String[] s = exchanges.get(index).split("-");
                        if (Arrays.asList(MqConstants.EXCHANGE_TYPES).contains(s[1])){
                            try{
                                switch (s[1]) {
                                    case "fanout":
                                        String str = o.toString();
                                        String queueBeanName = str.contains("Queue") ? str : str + "Queue";
                                        String exchangeName = exchanges.get(index).split("-")[0].contains("Exchange") ?
                                                exchanges.get(index).split("-")[0] : exchanges.get(index).split("-")[0] + "Exchange";
                                        FanoutExchange fanoutExchange = (FanoutExchange) applicationContext.getBean(exchangeName);
                                        Queue queue = registerBean(applicationContext, queueBeanName, Queue.class, queueBeanName);
                                        rabbitAdmin.declareQueue(queue);
                                        rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(fanoutExchange));
                                        break;
                                    case "direct":
                                        break;
                                    case "topic":
                                        String str1 = o.toString();
                                        str = str1.split("-")[0];
                                        queueBeanName = str.contains("Queue") ? str : str + "Queue";
                                        exchangeName = exchanges.get(index).split("-")[0].contains("Exchange") ?
                                                exchanges.get(index).split("-")[0] : exchanges.get(index).split("-")[0] + "Exchange";
                                        TopicExchange topicExchange = (TopicExchange) applicationContext.getBean(exchangeName);
                                        queue = registerBean(applicationContext, queueBeanName, Queue.class, queueBeanName);
                                        rabbitAdmin.declareQueue(queue);
                                        rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(topicExchange).with(str1.split("-")[1]));
                                        break;
                                    case "delay":
                                        break;
                                    case "formatDelay":
                                        break;
                                    default: {
                                    }
                                }
                            }catch (Exception eee){
                                eee.printStackTrace();
                                continue;
                            }

                        }
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                } finally {
                    index++;
                    continue;
                }
            }

        }
    }

    /**
     * 主动向Spring容器中注册bean
     *
     * @param applicationContext Spring容器
     * @param name               BeanName
     * @param clazz              注册的bean的类性
     * @param args               构造方法的必要参数，顺序和类型要求和clazz中定义的一致
     * @param <T>
     * @return 返回注册到容器中的bean对象
     */
    public <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clazz,
                              Object... args) {
        if (applicationContext.containsBean(name)) {
            Object bean = applicationContext.getBean(name);
            if (bean.getClass().isAssignableFrom(clazz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName 重复 " + name);
            }
        }


        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        for (Object arg : args) {
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        beanFactory.registerBeanDefinition(name, beanDefinition);
        return applicationContext.getBean(name, clazz);
    }

}

